package com.tengju.payment.infrastructure.shared.citic;

import com.lsy.baselib.crypto.algorithm.DESede;
import com.lsy.baselib.crypto.algorithm.RSA;
import com.lsy.baselib.crypto.exception.CipherUtilException;
import com.lsy.baselib.crypto.exception.DESedeException;
import com.lsy.baselib.crypto.exception.PKCS7SignatureException;
import com.lsy.baselib.crypto.exception.RSAException;
import com.lsy.baselib.crypto.protocol.PKCS7Signature;
import com.lsy.baselib.crypto.util.Base64;
import com.lsy.baselib.crypto.util.BytesUtil;
import com.lsy.baselib.crypto.util.CryptUtil;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;

import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertStore;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

//中信加密帮助类
@Slf4j
public class CiticCrypto {
    public static byte[] createRandomKey() {
        // 调用函数DESede.createKey创建随机密钥，或者自定义一个24字节长度的3DES密钥
        byte[] key = null;
        try {
            key = DESede.createKey(DESede.DESEDE_KEY_112_BIT);
        } catch (DESedeException ex) {
            log.error("产生随机密钥出错!", ex);
        }

        log.debug("------ 产生随机密钥长度:" + key.length);
        log.debug("------ 产生随机密钥 hex value:" + BytesUtil.binary2hex(key).toUpperCase());
        return key;
    }

    public static byte[] desEdeEncrypt(byte[] plainText, byte[] key) {
        byte[] iv = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                (byte) 0x00};
        byte[] cipherText = null;
        try {
            cipherText = DESede.encrypt(plainText, key, iv);
        } catch (DESedeException ex) {
            log.error("DESede加密出错", ex);
        }

        return cipherText;
    }

    public static byte[] desEdeDecrypt(byte[] cipherText, byte[] key) {
        byte[] iv = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                (byte) 0x00};
        byte[] plainText = null;
        try {
            plainText = DESede.decrypt(cipherText, key, iv);
        } catch (DESedeException ex) {
            log.error("DESede解密出错", ex);
        }

        return plainText;
    }

    public static X509Certificate createX509Cer(String cerContent) {
        byte[] base64EncodedCert = cerContent.getBytes(StandardCharsets.UTF_8);
        log.debug("------ base64EncodedCert=" + new String(base64EncodedCert));
        X509Certificate signerCertificate = null;
        try {
            signerCertificate = CryptUtil.generateX509Certificate(Base64.decode(base64EncodedCert));
            log.debug("------ signerCertificate=" + signerCertificate);
        } catch (Exception ex) {
            log.error("generateX509Certificate出错!", ex);
            return null;
        }

        return signerCertificate;
    }

    public static PublicKey getCerPublicKey(String cerContent) {
        X509Certificate signerCertificate = null;
        signerCertificate = createX509Cer(cerContent);
        PublicKey pubKey = signerCertificate.getPublicKey();
        log.debug("------ pubKey=" + pubKey);
        return pubKey;
    }

    public static PrivateKey getCerPrivateKey(String privateCerContent, String privateKeyPwdContent) {
        char[] keyPassword = null;
        PrivateKey signerPrivateKey = null;
        // 获取私钥密码
        keyPassword = privateKeyPwdContent.toCharArray();
        log.debug("------ keyPassword=" + new String(keyPassword));
        // 读取私钥
        byte[] base64EncodedPrivateKey = privateCerContent.getBytes(StandardCharsets.UTF_8);
        log.debug("------ base64EncodedPrivatekey=" + new String(base64EncodedPrivateKey));
        try {
            signerPrivateKey = CryptUtil.decryptPrivateKey(Base64.decode(base64EncodedPrivateKey), keyPassword);
            log.info("------ signerPrivatekey=" + signerPrivateKey);
        } catch (CipherUtilException ex) {
            log.error("decryptPrivateKey出错!", ex);
            return null;
        }

        // 清除私钥解密密码
        for (int i = 0; i < keyPassword.length; i++) {
            keyPassword[0] = 0;
        }

        return signerPrivateKey;
    }

    public static byte[] encryptByCerPublicKey(byte[] plainText, String receiverCerContent) {
        byte[] cipherText = null;
        PublicKey pubKey = getCerPublicKey(receiverCerContent);
        try {
            cipherText = RSA.encrypt(plainText, pubKey.getEncoded());
        } catch (RSAException ex) {
            log.error("RSA.encrypt出错!", ex);
            return null;
        }

        return cipherText;
    }

    private static byte[] decryptByCerPrivateKey(byte[] cipherText, String privateKeyContent, String privateKeyPwdContent) {
        byte[] plainText = null;
        PrivateKey privateKey = getCerPrivateKey(privateKeyContent, privateKeyPwdContent);

        try {
            plainText = RSA.decrypt(cipherText, privateKey.getEncoded());
        } catch (RSAException ex) {
            log.error("RSA.decrypt出错!", ex);
            return null;
        }

        return plainText;
    }

    public static byte[] linkByteArrays(byte[] arr1, byte[] arr2) {
        int n1 = arr1.length;
        int n2 = arr2.length;
        byte[] newArr = new byte[n1 + n2];
        System.arraycopy(arr1, 0, newArr, 0, n1);
        System.arraycopy(arr2, 0, newArr, n1, n2);
        return newArr;
    }

    public static byte[] linkByteArrays(byte[] arr1, byte[] arr2, byte[] arr3) {
        int n1 = arr1.length;
        int n2 = arr2.length;
        int n3 = arr3.length;
        byte[] newArr = new byte[n1 + n2 + n3];
        System.arraycopy(arr1, 0, newArr, 0, n1);
        System.arraycopy(arr2, 0, newArr, n1, n2);
        System.arraycopy(arr3, 0, newArr, n1 + n2, n3);
        return newArr;
    }

    public static byte[] linkByteArrays(byte[] arr1, byte[] arr2, byte[] arr3, byte[] arr4) {
        int n1 = arr1.length;
        int n2 = arr2.length;
        int n3 = arr3.length;
        int n4 = arr4.length;
        byte[] newArr = new byte[n1 + n2 + n3 + n4];
        System.arraycopy(arr1, 0, newArr, 0, n1);
        System.arraycopy(arr2, 0, newArr, n1, n2);
        System.arraycopy(arr3, 0, newArr, n1 + n2, n3);
        System.arraycopy(arr4, 0, newArr, n1 + n2 + n3, n4);
        return newArr;
    }

    public static String requestMessageSign(byte[] plainText, String senderPwd, String senderKey, String senderCer) {
        PrivateKey privateKey = getCerPrivateKey(senderKey, senderPwd);
        X509Certificate signerCertificate = createX509Cer(senderCer);
        byte[] signature = null;
        try {
            signature = sign(plainText, privateKey, signerCertificate, null, false);
            log.debug("------ signature(长度=" + signature.length + ")=" + new String(signature));
        } catch (PKCS7SignatureException ex) {
            log.error("PKCS7Signature.sign出错!");
            return null;
        } catch (Exception e) {
            log.error("PKCS7Signature.sign出错!",e);
            return null;
        }

        String b64StrSignature = new String(Base64.encode(signature));
        log.debug("------ 签名结果(base64,长度=" + b64StrSignature.length() + "):" + b64StrSignature);
        return b64StrSignature;
    }


    public static byte[] sign(byte[] message, PrivateKey signerPrivatekey, X509Certificate signerCertificate, X509Certificate[] chain, boolean attach) throws PKCS7SignatureException {
        byte[] signedMessage = (byte[])null;

        try {
            CMSSignedDataGenerator cms = new CMSSignedDataGenerator();
            cms.addSigner(signerPrivatekey, signerCertificate, CMSSignedDataGenerator.DIGEST_SHA1);
            List listchain = new ArrayList();
            if (chain != null) {
                for(int i = 0; i < chain.length; ++i) {
                    listchain.add(chain[i]);
                }
            }

            CertStore cstore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(listchain), "BC");
            cms.addCertificatesAndCRLs(cstore);
            CMSProcessable msg = new CMSProcessableByteArray(message);
            CMSSignedData cmsSignedData = cms.generate(msg, attach, "BC");
            signedMessage = cmsSignedData.getEncoded();
            return signedMessage;
        } catch (Exception var11) {
            throw new PKCS7SignatureException("对消息进行签名失败", var11);
        }
    }


    public static boolean validateRequestMessage(byte[] plainText, String b64StrSign, String senderCer) {
        long beginTime = System.currentTimeMillis();
        PublicKey pubKey = getCerPublicKey(senderCer);
        log.debug("------ pubKey=" + pubKey);

        boolean checkResult = PKCS7Signature.verifyDetachedSignature(plainText, Base64.decode(b64StrSign.getBytes(StandardCharsets.UTF_8)), pubKey);
        log.debug("------ 验签结果=" + checkResult);

        long endTime = System.currentTimeMillis();
        log.debug("------ 接收方验证报文签名耗时(ms):" + (endTime - beginTime));
        return checkResult;
    }

    public static String decryptResponse(byte[] cipherTextMessage, String receiverKey, String receiverPwd) {
        String sMsg = new String(cipherTextMessage);
        String sKey = null;
        String openTag = "<sessionkey>";
        String closeTag = "</sessionkey>";
        byte[] cipherData = null;

        int start = sMsg.indexOf(openTag);
        if (start != -1) {
            int end = sMsg.indexOf(closeTag, start + openTag.length());
            if (end != -1) {
                sKey = sMsg.substring(start + openTag.length(), end);
                cipherData = new byte[cipherTextMessage.length - end - closeTag.length()];
                System.arraycopy(cipherTextMessage, end + closeTag.length(), cipherData, 0, cipherTextMessage.length - end - closeTag.length());
                log.debug("------ sKey=" + sKey);
                log.debug("------ cipherData=" + new String(cipherData));
            } else {
                log.warn("找不到标签" + closeTag);
                return null;
            }
        } else {
            log.warn("找不到标签" + openTag);
            return null;
        }

        byte[] sessionKeyPlainText = CiticCrypto.decryptByCerPrivateKey(Base64.decode(sKey.getBytes()), receiverKey, receiverPwd);
        byte[] decryptData = CiticCrypto.desEdeDecrypt(cipherData, sessionKeyPlainText);
        String sData = new String(decryptData);
        return sData;
    }
}

